﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;

namespace BasicWave
{
   
        /// <summary>
        /// la classe è una versione più specifica prendendo come base l'interfaccia aux 
        /// che permette di ricevere dati.
        /// 
        /// questa classe si occupa di scrivere il contenuto che manda in esecuzione
        /// su dei chunk di dimensioni prestabilite
        /// 
        /// </summary>
        public class CAuxExportChannel : IAuxChannel, IDisposable
        {
            /// <summary>
            /// Delegato che viene chiamato alla creazione di un nuovo chunk da parte ddella classe
            /// </summary>
            /// <param name="file">Restituisce il nome del file</param>
            /// <param name="id">Restituisce un numero id associato al chunk</param>
            public delegate void CreatedChunk(string file, int id);
            /// <summary>
            /// Evento che viene chiamato a tutti i sottoscrittori per notificare la creazione d
            /// di un nuovo chunk wave
            /// </summary>
            public event CreatedChunk OnCreatedChunk;


            CMasterChannel m_master;

            const int BUFF_LEN = 3276800;


            /// <summary>
            /// misura massima del chunk wave
            /// </summary>
            const int CHUNK_SIZE = 2048000; //8Mb
            FileStream fs;
            BinaryWriter bw;
            int byte_writed = 0;
            bool _isFileCreated = false;





            int numSrc = 0;


            /// <summary>
            /// la funzione oltre ad essere il costruttore
            /// inviare al master la registrazione di un aux channel
            /// a cui inviare copia dati che manda in esecuzione
            /// </summary>
            /// <param name="aMaster"></param>
            public CAuxExportChannel(ref CMasterChannel aMaster)
            {
                m_master = aMaster;

                //informo il master che deve riconoscermi come aux
                //e inviare dati
                m_master.RegisteredAux(this);


            }

            public void DeleteChunk(string aFile)
            {
                try
                {
                   File.Delete(aFile);
                }
                catch (FileNotFoundException noFound)
                {
                    throw noFound;
                }
            }


            public void Dispose()
            {
                if (m_master != null)
                    m_master.UnregisteredAux(this);

                if (_isFileCreated)
                    CloseChunk();



            }


            /// <summary>
            /// scrive i dati sul file aperto
            /// </summary>
            /// <param name="buff"></param>
            /// <param name="size"></param>
            public void RawData(byte[] buff, int size)
            {

                if (_isFileCreated ==false)
                {
                    CreateNewChunk(numSrc++);
                    _isFileCreated = true;


                }


                for (int i = 0; i < size; i++)
                {
                    bw.Write(buff[i]);

                }

                byte_writed += size;





                if (byte_writed >= CHUNK_SIZE)
                {
                    CloseChunk();

                    byte_writed = 0;
                    _isFileCreated = false;


                    ReshapeWaveHeader(numSrc-1);



                    //lancia l'evento ai sottoscrittori
                    if (OnCreatedChunk != null)
                        OnCreatedChunk("chunk_num_" + numSrc.ToString() + ".wav", numSrc);

                }

            }


            
        

            /// <summary>
            /// questa funzione la chiamo dopo la chiusura del file...
            /// prima di riaprirne un altro
            /// si occupa di settare correttamente gli header wave in
            /// base alla dimensione del file e ad altri parametri
            /// </summary>
            private void ReshapeWaveHeader(int num)
            {
                //apre file, legge solo i primi 44 byte
                byte[] buff = new byte[44];
                float lenFile = 0;
                FileStream fs = new FileStream("chunk_num_" + num.ToString() + ".wav", FileMode.Open, FileAccess.ReadWrite,  FileShare.Read);
                BinaryReader br = new BinaryReader(fs);
                BinaryWriter bw = new BinaryWriter(fs);


                fs.Position = 0;
                lenFile = fs.Length;

                
                buff = br.ReadBytes(44);


               


               //rsiscrivo dimensione totale sul chunk alla posizione 4 del file
                fs.Position = 4;
                bw.Write((int)lenFile - 8) ;


                //reshaping byte rate
                fs.Position = 28;
                bw.Write((int)44100 * 2 * 16 / 8);

                //block align
                fs.Position = 32;
                bw.Write((short)2 * 16 / 8);

                fs.Position = 34;
                bw.Write((short)16);



                //subchunk2size

                fs.Position = 40;
                bw.Write((int)(lenFile - 44));




                fs.Close();
                br.Close();
                bw.Close();
                fs.Dispose();

                
            }

            private void DeleteAllChunk()
            {
                //TODO: da fare
#if DEBUF

#else
                
#endif
            }


            //crea il file wav con una basilare intestazione
            private void CreateNewChunk(int num)
            {
                fs = new FileStream("chunk_num_" + num.ToString() + ".wav", FileMode.Create,  FileAccess.Write, FileShare.ReadWrite);
                bw = new BinaryWriter(fs);


                //scrivo l'intestazione del file wav
                Byte[] buff = new Byte[44];

                ASCIIEncoding.ASCII.GetBytes("RIFF", 0, 4, buff, 0);

                buff[4] = 0x00;
                buff[5] = 0x31;
                buff[6] = 0xFF;
                buff[7] = 0xF8;

                ASCIIEncoding.ASCII.GetBytes("WAVEfmt ", 0, 8, buff, 8);


                //pcm
                buff[16] = 0x10;
                buff[17] = 0;
                buff[18] = 0;
                buff[19] = 0;

                //linear
                buff[20] = 0x01;
                buff[21] = 0;

                //num channel
                buff[22] = 0x02;
                buff[23] = 0;


                //sample rate
                buff[24] = 0x44;
                buff[25] = 0xAC;
                buff[26] = 0;
                buff[27] = 0;


                //bit rate
                buff[28] = 0x10;
                buff[29] = 0xB1;
                buff[30] = 0x02;
                buff[31] = 0x00;

                //block align
                buff[32] = 0x04;
                buff[33] = 0x00;

                buff[34] = 0x10;
                buff[35] = 0x00;

                ASCIIEncoding.ASCII.GetBytes("data", 0, 4, buff, 36);



                buff[40] = 0xBC;
                buff[41] = 0x7F;
                buff[42] = 0xFF;
                buff[43] = 0xFF;



                bw.Write(buff);

            }

            private void CloseChunk()
            {
             
                long len = fs.Length;

                try
                {
                    fs.Unlock(0, len);
                }
                catch
                {
                }
                fs.Close();
               
                bw.Close();

               

            }




          
        }
    
}
